package com.ailanyin.modules.flowable.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.ailanyin.common.base.exception.Assert;
import com.ailanyin.common.database.result.CommonPage;
import com.ailanyin.common.flowable.constant.ProcessConstants;
import com.ailanyin.common.flowable.enums.FormType;
import com.ailanyin.common.flowable.factory.FlowServiceFactory;
import com.ailanyin.common.flowable.utils.ModelUtils;
import com.ailanyin.common.security.utils.SecurityUtil;
import com.ailanyin.modules.flowable.domain.bo.WfModelBo;
import com.ailanyin.modules.flowable.domain.dto.WfMetaInfoDto;
import com.ailanyin.modules.flowable.domain.vo.WfFormVo;
import com.ailanyin.modules.flowable.domain.vo.WfModelVo;
import com.ailanyin.modules.flowable.service.IWfDeployFormService;
import com.ailanyin.modules.flowable.service.IWfFormService;
import com.ailanyin.modules.flowable.service.IWfModelService;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ModelQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * @author ailanyin
 * @version 1.0
 * @since 2022/12/13 0013 上午 11:33
 */
@Service
public class WfModelServiceImpl extends FlowServiceFactory implements IWfModelService {


    @Autowired
    private IWfFormService formService;
    @Autowired
    private IWfDeployFormService deployFormService;

    @Override
    public CommonPage<WfModelVo> list(WfModelBo modelBo, Integer pageNum, Integer pageSize) {

        ModelQuery modelQuery = repositoryService.createModelQuery().latestVersion().orderByCreateTime().desc();
        // 构建查询条件
        createSelectQuery(modelBo, modelQuery);
        // 执行查询
        long pageTotal = modelQuery.count();
        if (pageTotal <= 0) {
            return new CommonPage<WfModelVo>(new ArrayList<>(), pageTotal);
        }
        int offset = pageSize * (pageNum - 1);
        List<Model> modelList = modelQuery.listPage(offset, pageSize);
        List<WfModelVo> modelVoList = getWfModelVoList(modelList);
        return new CommonPage<WfModelVo>(modelVoList, pageTotal);
    }


    @Override
    public List<WfModelVo> list(WfModelBo modelBo) {
        ModelQuery modelQuery = repositoryService.createModelQuery().latestVersion().orderByCreateTime().desc();
        createSelectQuery(modelBo, modelQuery);
        List<Model> modelList = modelQuery.list();
        return getWfModelVoList(modelList);
    }

    @Override
    public CommonPage<WfModelVo> historyList(WfModelBo modelBo, Integer pageNum, Integer pageSize) {
        ModelQuery modelQuery = repositoryService.createModelQuery()
                .modelKey(modelBo.getModelKey())
                .orderByModelVersion()
                .desc();
        // 执行查询（不显示最新版，-1）
        long pageTotal = modelQuery.count() - 1;
        if (pageTotal <= 0) {
            return new CommonPage<WfModelVo>(new ArrayList<>(), pageTotal);
        }
        // offset+1，去掉最新版
        int offset = 1 + pageSize * (pageNum - 1);
        List<Model> modelList = modelQuery.listPage(offset, pageSize);
        List<WfModelVo> wfModelVoList = getWfModelVoList(modelList);
        return new CommonPage<WfModelVo>(wfModelVoList, pageTotal);
    }

    @Override
    public WfModelVo getModel(String modelId) {
        // 获取流程模型
        Model model = repositoryService.getModel(modelId);
        if (ObjectUtil.isNull(model)) {
            Assert.fail("流程模型不存在！");
        }

        WfModelVo modelVo = createModelVo(model);
        modelVo.setBpmnXml(queryBpmnXmlById(modelId));
        WfMetaInfoDto metaInfo = JSONUtil.toBean(model.getMetaInfo(), WfMetaInfoDto.class);
        if (metaInfo != null) {
            modelVo.setDescription(metaInfo.getDescription());
            modelVo.setFormType(metaInfo.getFormType());
            modelVo.setFormId(metaInfo.getFormId());
            if (FormType.PROCESS.getType().equals(metaInfo.getFormType())) {
                WfFormVo wfFormVo = formService.queryById(metaInfo.getFormId());
                modelVo.setContent(wfFormVo.getContent());
            }
        }
        return modelVo;
    }

    @Override
    public String queryBpmnXmlById(String modelId) {
        return StrUtil.utf8Str(repositoryService.getModelEditorSource(modelId));
    }

    @Override
    public void insertModel(WfModelBo modelBo) {
        Model model = repositoryService.newModel();
        model.setName(modelBo.getModelName());
        model.setKey(modelBo.getModelKey());
        model.setCategory(modelBo.getCategory());
        String metaInfo = buildMetaInfo(new WfMetaInfoDto(), modelBo.getDescription());
        model.setMetaInfo(metaInfo);
        // 保存流程模型
        repositoryService.saveModel(model);
    }

    @Override
    public void updateModel(WfModelBo modelBo) {
        // 根据模型Key查询模型信息
        Model model = repositoryService.getModel(modelBo.getModelId());
        if (ObjectUtil.isNull(model)) {
            Assert.fail("流程模型不存在！");
        }
        model.setCategory(modelBo.getCategory());
        WfMetaInfoDto metaInfoDto = JSONUtil.toBean(model.getMetaInfo(), WfMetaInfoDto.class);
        String metaInfo = buildMetaInfo(metaInfoDto, modelBo.getDescription());
        model.setMetaInfo(metaInfo);
        // 保存流程模型
        repositoryService.saveModel(model);
    }

    @Override
    public void saveModel(WfModelBo modelBo) {
        // 查询模型信息
        Model model = repositoryService.getModel(modelBo.getModelId());
        if (ObjectUtil.isNull(model)) {
            Assert.fail("流程模型不存在！");
        }
        BpmnModel bpmnModel = ModelUtils.getBpmnModel(modelBo.getBpmnXml());
        if (ObjectUtil.isEmpty(bpmnModel)) {
            Assert.fail("获取模型设计失败！");
        }
        String processName = bpmnModel.getMainProcess().getName();
        // 获取开始节点
        StartEvent startEvent = ModelUtils.getStartEvent(bpmnModel);
        if (ObjectUtil.isNull(startEvent)) {
            Assert.fail("开始节点不存在，请检查流程设计是否有误！");
        }
        // 获取开始节点配置的表单Key
        if (StrUtil.isBlank(startEvent.getFormKey())) {
            Assert.fail("请配置流程表单");
        }
        Model newModel;
        if (Boolean.TRUE.equals(modelBo.getNewVersion())) {
            newModel = repositoryService.newModel();
            newModel.setName(processName);
            newModel.setKey(model.getKey());
            newModel.setCategory(model.getCategory());
            newModel.setMetaInfo(model.getMetaInfo());
            newModel.setVersion(model.getVersion() + 1);
        } else {
            newModel = model;
            // 设置流程名称
            newModel.setName(processName);
        }
        // 保存流程模型
        repositoryService.saveModel(newModel);
        // 保存 BPMN XML
        repositoryService.addModelEditorSource(newModel.getId(), ModelUtils.getBpmnXml(bpmnModel));
    }

    @Override
    public void latestModel(String modelId) {
        // 获取流程模型
        Model model = repositoryService.getModel(modelId);
        if (ObjectUtil.isNull(model)) {
            Assert.fail("流程模型不存在！");
        }
        String bpmnXml = queryBpmnXmlById(modelId);
        Integer latestVersion = repositoryService.createModelQuery()
                .modelKey(model.getKey())
                .latestVersion()
                .singleResult()
                .getVersion();
        if (model.getVersion().equals(latestVersion)) {
            Assert.fail("当前版本已是最新版！");
        }
        Model newModel = repositoryService.newModel();
        newModel.setName(model.getName());
        newModel.setKey(model.getKey());
        newModel.setCategory(model.getCategory());
        newModel.setMetaInfo(model.getMetaInfo());
        newModel.setVersion(latestVersion + 1);
        // 保存流程模型
        repositoryService.saveModel(newModel);
        // 保存 BPMN XML
        repositoryService.addModelEditorSource(newModel.getId(), StrUtil.utf8Bytes(bpmnXml));
    }

    @Override
    public void deleteByIds(Collection<String> ids) {
        for (String id : ids) {
            repositoryService.deleteModel(id);
        }
    }

    @Override
    public boolean deployModel(String modelId) {
        // 获取流程模型
        Model model = repositoryService.getModel(modelId);
        if (ObjectUtil.isNull(model)) {
            throw new RuntimeException("流程模型不存在！");
        }
        // 获取流程图
        String bpmnXml = queryBpmnXmlById(modelId);
        BpmnModel bpmnModel = ModelUtils.getBpmnModel(bpmnXml);
        String processName = model.getName() + ProcessConstants.SUFFIX;
        // 部署流程
        Deployment deployment = repositoryService.createDeployment()
                .name(model.getName() + "-" + model.getVersion())
                .key(model.getKey())
                .addBpmnModel(processName, bpmnModel)
                .category(model.getCategory())
                .deploy();
        // 保存部署表单
        return deployFormService.saveInternalDeployForm(deployment.getId(), bpmnModel);
    }

    private void createSelectQuery(WfModelBo modelBo, ModelQuery modelQuery) {
        // 构建查询条件
        if (StrUtil.isNotBlank(modelBo.getModelKey())) {
            modelQuery.modelKey(modelBo.getModelKey());
        }
        if (StrUtil.isNotBlank(modelBo.getModelName())) {
            modelQuery.modelNameLike("%" + modelBo.getModelName() + "%");
        }
        if (StrUtil.isNotBlank(modelBo.getCategory())) {
            modelQuery.modelCategory(modelBo.getCategory());
        }
    }

    private List<WfModelVo> getWfModelVoList(List<Model> modelList) {
        List<WfModelVo> modelVoList = new ArrayList<>(modelList.size());
        modelList.forEach(model -> {
            WfModelVo modelVo = createModelVo(model);
            WfMetaInfoDto metaInfo = JSONUtil.toBean(model.getMetaInfo(), WfMetaInfoDto.class);
            if (metaInfo != null) {
                modelVo.setDescription(metaInfo.getDescription());
                modelVo.setFormType(metaInfo.getFormType());
                modelVo.setFormId(metaInfo.getFormId());
            }
            modelVoList.add(modelVo);
        });
        return modelVoList;
    }

    private WfModelVo createModelVo(Model model) {
        WfModelVo modelVo = new WfModelVo();
        modelVo.setModelId(model.getId());
        modelVo.setModelName(model.getName());
        modelVo.setModelKey(model.getKey());
        modelVo.setCategory(model.getCategory());
        modelVo.setCreateTime(model.getCreateTime());
        modelVo.setVersion(model.getVersion());
        return modelVo;
    }

    /**
     * 构建模型扩展信息
     *
     * @return json
     */
    private String buildMetaInfo(WfMetaInfoDto metaInfo, String description) {
        // 只有非空，才进行设置，避免更新时的覆盖
        if (StrUtil.isNotEmpty(description)) {
            metaInfo.setDescription(description);
        }
        if (StrUtil.isNotEmpty(metaInfo.getCreateUser())) {
            metaInfo.setCreateUser(SecurityUtil.getLoginUsername());
        }
        return JSONUtil.toJsonStr(metaInfo);
    }
}
